home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 August: Tool Chest / Dev.CD Aug 98 TC.toast / Sample Code / Snippets / Sound / SoundLevel / SoundLevel.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-10  |  27.1 KB  |  1,199 lines  |  [TEXT/MPS ]

  1. /*    ---------------------------------------------------------------------------------
  2.     SoundLevel
  3.     
  4.     Ken Bereskin
  5.     March 30, 1992
  6.     
  7.     A simple application that echos the sound input level on the microphone using
  8.     a display much like a VCR peak-hold bar meter.
  9.     
  10.     Its a fun app, to use up those wasted background cycles and seems to amuse
  11.     people to no end. At 40k, its not a major memory hog so pop it in your Startup
  12.     Items folder and enjoy.
  13.     
  14.     The About Box introduces a new power user short cut. Clap hard enough to raise
  15.     the sound threshold about ≈ 95% and the dialog will dismiss. No need to reach
  16.     and strain for that enter key anymore!
  17.     
  18.     Caveats:
  19.         • Not much error checking.
  20.         • Too many globals.
  21.         • Saves the window position back to the app resource fork.
  22.         • Requires a Sound Input Device (MacRecorder will do just fine)
  23.         • It keeps the sound input device open with r/w so you won’t be able
  24.           to use other sound input apps unless you quit.
  25.     
  26.     Have fun and let me know what you think.      
  27.     
  28.     Copyright ©1992 Apple Canada Inc.
  29. */
  30.  
  31. /*    ---------------------------------------------------------------------------------
  32.     ToolBox header files
  33. */
  34. #include <Types.h>
  35. #include <QuickDraw.h>
  36. #include <Resources.h>
  37. #include <Windows.h>
  38. #include <Fonts.h>
  39. #include <Events.h>
  40. #include <TextEdit.h>
  41. #include <Dialogs.h>
  42. #include <Menus.h>
  43. #include <Memory.h>
  44. #include <Desk.h>
  45. #include <ToolUtils.h>
  46. #include <OSUtils.h>
  47. #include <Errors.h>
  48. #include <Folders.h>
  49. #include <OSEvents.h>
  50. #include <SegLoad.h>
  51. #include <Traps.h>
  52. #include <SoundInput.h>
  53. #include <GestaltEqu.h>
  54.  
  55.  
  56. /*    ---------------------------------------------------------------------------------
  57.     Forward reference prototypes
  58. */
  59. void    Initialize(void);
  60. void    SetupEnvironment(void);
  61. void    SetupMemory(void);
  62. void    SetupMenus(void);
  63. void    DoEvents(void);
  64. void    DoMouseDown (EventRecord *event);
  65. void    DoKeyDown (EventRecord *event);
  66. void    DoUpdateWindow (WindowPtr window);
  67. void    DoActivateWindow (Boolean activate, WindowPtr window);
  68. void    DoMenuCommand(short theMenu, short theItem);
  69. void    DoContentClick(EventRecord *event, WindowPtr window);
  70. void    DoSuspend(EventRecord *event);
  71. void    DoResume(EventRecord *event);
  72. void    DoIdle(void);
  73. void    DoDeskAccessory(short whichMenu, short whichItem);
  74. void    NewLevelWindow(void);
  75. void    CleanUp(void);
  76. void    DoAbout(void);
  77. Boolean    OptionTest(void);
  78. Boolean CmdTest(void);
  79. Boolean ShiftTest(void);
  80. Boolean    ColourQDExists (void);
  81. Boolean ColourQDIsOn (void);
  82. short    ColourDepth (void);
  83. void     TwitchToFinder(void);
  84. void    DrawTheMeter(Rect *meterRect, short numElements, short *peakLevel, short *lastLevel, long *peakTimeout, short meterLevel, short redZone, RGBColor *blueRGB, RGBColor *redRGB, RGBColor *blackRGB, Boolean useColour);
  85. void     GetBarRect(Rect *meterRect, short whichBar, Rect *barRect);
  86. void     InitLevelMeter (void);
  87. void    CloseLevelMeter (void);
  88. OSErr     OpenTheSoundDevice(void);
  89. void     DrawOneBar(Rect *meterRect, short whichBar, Boolean useColour);
  90. pascal  Boolean AboutFilterProc(DialogPtr dialog, EventRecord *event, short *item);
  91. void     HiliteItem(DialogPtr dialog, short theitem);
  92. void    UpdateAboutDialog(WindowPtr window);
  93.  
  94. /*    ---------------------------------------------------------------------------------
  95.     Constants
  96. */
  97. #define kMaxVolume                255
  98. #define kPeakTimeoutTicks        60            // number of ticks needed to timeout peak
  99.  
  100. #define kMenuBarID    128
  101.  
  102. #define kAppleMenu        128
  103. #define kAboutItem        1
  104.  
  105. #define kFileMenu        129
  106. #define kQuitItem        1
  107.  
  108. #define kGestaltLevelMeter    'SnLv'
  109.  
  110. /*    ---------------------------------------------------------------------------------
  111.     Global variables
  112. */
  113. Boolean        WNEIsImplemented;                // is WaitNextEvent avaiable
  114. Boolean     gQuitFlag = false;                // set to force a quit
  115. WindowPtr     gLevelWindow;                    // sound level window pointer
  116. RGBColor    blueRGB, redRGB, blackRGB;        // rgb colours used for drawing meter
  117. short        gNumElements;                    // number of elements in the meter
  118. long        gPeakTimeOut;
  119. short        gPeakLevel;
  120. short         gLastLevel;
  121. short        gRedZone;
  122. Rect        gMeterRect;
  123. long        gSoundInputRefNum;
  124. Boolean        gUseColour;
  125.  
  126. #ifdef powerc
  127.    QDGlobals    qd;
  128. #endif
  129.  
  130. /*    ---------------------------------------------------------------------------------
  131.     Main
  132.     
  133.     Application main entry point
  134. */
  135. main()
  136. {
  137.     Initialize();                    // do application initialization
  138.         
  139.     NewLevelWindow();                // create the level window
  140.     InitLevelMeter();                // start up the level meter
  141.  
  142.     DoUpdateWindow(gLevelWindow);
  143.     
  144.     while (!gQuitFlag)                // handle events until quit
  145.         DoEvents();
  146.  
  147.     CleanUp();                        // clean up before quitting
  148. }
  149.  
  150. /*    ---------------------------------------------------------------------------------
  151.     Intialize
  152.     
  153.     First time application initialization
  154. */
  155.  
  156. #pragma segment Initialize
  157.  
  158. static void
  159. Initialize()
  160. {
  161.     InitGraf(&qd.thePort);
  162.     InitFonts();
  163.     InitWindows();
  164.     InitMenus();
  165.     TEInit();
  166.     InitDialogs(nil);
  167.     InitCursor();
  168.     
  169.     FlushEvents(everyEvent, 0);
  170.         
  171.     SetupMemory();
  172.     SetupEnvironment();
  173.     SetupMenus();
  174.     
  175.     gUseColour = ColourDepth() >= 4;
  176.     
  177. }
  178.  
  179. /*    ---------------------------------------------------------------------------------
  180.     SetupEnvironment
  181.     
  182.     This routine will determine the hardware and software operating environment
  183. */
  184.  
  185. #define RequestedVersion    1
  186.  
  187. static void
  188. SetupEnvironment()
  189. {
  190.     WNEIsImplemented = true;
  191. }
  192.  
  193. /*
  194.     --- SetupMemory ------------------------------------------------------------------------------------
  195.     This routine will allocate some master pointers, grow the heap,
  196.     and set up a grow zone function.
  197. */
  198. static void
  199. SetupMemory()
  200. {
  201.     MaxApplZone();
  202.     MoreMasters();
  203.     MoreMasters();
  204. }
  205.  
  206.  
  207. /*
  208.     --- SetupMenus ------------------------------------------------------------------------------------
  209.     This routine will set up the LeadSheet menus.
  210. */
  211.  
  212. static void
  213. SetupMenus()
  214. {
  215.     Handle menuBarHndl;
  216.     
  217.     // load up the apple and file menu
  218.     menuBarHndl = GetNewMBar(kMenuBarID);        // read menus into menu bar
  219.     SetMenuBar(menuBarHndl);                    // install menus
  220.     DisposeHandle(menuBarHndl);
  221.     
  222.     AppendResMenu(GetMenuHandle(kAppleMenu), 'DRVR');        // add DA names to Apple menu
  223.     
  224.     DrawMenuBar();
  225. }
  226.  
  227. #pragma segment Main
  228.  
  229. /*    ---------------------------------------------------------------------------------
  230.     DoEvents
  231.     
  232.     This is the LaunchPad Main Event Loop. We fetch events from the event queue, 
  233.     and call the proper handlers for the given event class. 
  234. */
  235.  
  236. #define kSleepTicks    0
  237.  
  238. static void
  239. DoEvents()
  240. {
  241.     Boolean newEvent;                // is there an event in the queue
  242.     EventRecord event;                // the event record    
  243.  
  244.     /*
  245.         Look for an event in the event queue.
  246.     */
  247.     if (WNEIsImplemented) {
  248.         newEvent = WaitNextEvent(everyEvent, &event, kSleepTicks, nil);
  249.     } else {
  250.         SystemTask();
  251.         newEvent = GetNextEvent(everyEvent, &event);
  252.     }
  253.         
  254.     DoIdle();            // idle processing
  255.     
  256.     /*    Dispatch on the type of event */    
  257.     switch(event.what) {
  258.         case mouseDown:
  259.             DoMouseDown(&event);
  260.             break;
  261.         case keyDown:
  262.         case autoKey:
  263.             DoKeyDown(&event);
  264.             break;
  265.         case updateEvt:
  266.             DoUpdateWindow((WindowPtr)event.message);
  267.             break;
  268.         case activateEvt:
  269.             DoActivateWindow(event.modifiers & activeFlag, (WindowPtr)event.message);
  270.             break;
  271.         case diskEvt:
  272.             break;
  273.         case osEvt:
  274.             /*
  275.                 Check for suspend or resume events (the only ones now defined)
  276.             */
  277.             if ((event.message >> 24) == suspendResumeMessage) {
  278.                 if (event.message & resumeFlag)
  279.                     DoResume(&event);
  280.                 else
  281.                     DoSuspend(&event);
  282.             }
  283.             break;
  284.         case highLevelEventMask:
  285.             break;
  286.     }
  287.     
  288.     DoIdle();            // more idle processing
  289. }
  290.  
  291. /*    --- DoMouseDown ----------------------------------------------------------------------------------
  292.     This routine handles mouseDown events.
  293. */
  294. static void
  295. DoMouseDown(event)
  296. EventRecord *event;
  297. {
  298.     short whichPart;
  299.     WindowPtr window;
  300.     Rect dragBounds;
  301.     long selection;
  302.     short theMenu, theItem;
  303.     
  304.     /*
  305.         Determine where the mouseDown event occurred
  306.     */
  307.     whichPart = FindWindow(event->where, &window);
  308.     
  309.     switch(whichPart) {
  310.         case inMenuBar:
  311.             selection = MenuSelect(event->where);
  312.             theMenu = HiWord(selection);
  313.             theItem = LoWord(selection);
  314.             DoMenuCommand(theMenu, theItem);
  315.             break;
  316.         case inSysWindow:
  317.             SystemClick(event, window);
  318.             break;
  319.         case inContent:
  320.             DoContentClick(event, window);
  321.             break;
  322.         case inDrag:
  323.             dragBounds = qd.screenBits.bounds;
  324.             DragWindow(window, event->where, &dragBounds);
  325.             break;
  326.         case inGrow:
  327.             break;
  328.         case inGoAway:
  329.             break;
  330.         case inZoomIn:
  331.         case inZoomOut:
  332.             break;
  333.         case inDesk:        // a click in the desktop will deselect the current selection
  334.             break;
  335.     }
  336. }
  337.  
  338.  
  339. /*    ---------------------------------------------------------------------------------
  340.     DoKeyDown
  341.     
  342.     This routine handles keyDown events.
  343. */
  344. static void
  345. DoKeyDown (EventRecord *event)
  346. {
  347. #pragma unused(event)
  348.     gQuitFlag = true;
  349. }
  350.  
  351. /*    ---------------------------------------------------------------------------------
  352.     DoUpdateWindow
  353.  
  354.     This routine handles update events for the given window
  355. */
  356.  
  357. static void
  358. DoUpdateWindow (window)
  359. WindowPtr window;
  360. {
  361.     GrafPtr savePort;
  362.     Rect boundsRect;
  363.     short i;
  364.     
  365.     GetPort(&savePort);
  366.     
  367.     BeginUpdate(window);        // set up the clipRgn and visRgn
  368.     
  369.     SetPort(window);
  370.     
  371.     RGBForeColor(&blackRGB);
  372.     EraseRect(&window->portRect);
  373.     boundsRect = window->portRect;
  374.     InsetRect(&boundsRect, 2, 2);
  375.     PaintRect(&boundsRect);
  376.     
  377.     // redraw the bar graph frames up to the current level
  378.     for (i = 1; i <= gLastLevel; i++) {
  379.         DrawOneBar(&gMeterRect, i, gUseColour);
  380.     }
  381.     
  382.     EndUpdate(window);            // restore window regions
  383.     
  384.     SetPort(savePort);
  385. }
  386.  
  387. /*    --- DoActivateWindow ----------------------------------------------------------------------------------
  388.     This routine handles activate events for the given window
  389. */
  390. static void
  391. DoActivateWindow (activate, window)
  392. Boolean activate;
  393. WindowPtr window;
  394. {
  395. #pragma unused(activate, window)
  396. }
  397.  
  398. /*    ---------------------------------------------------------------------------------
  399.     DoMenuCommand
  400.     
  401.     This routine handles menu selection dispatching
  402. */
  403.  
  404. static void
  405. DoMenuCommand(short theMenu, short theItem)
  406. {
  407.     switch (theMenu) {
  408.         case kAppleMenu:
  409.             if (theItem == kAboutItem) {
  410.                 DoAbout();
  411.             } else {
  412.                 DoDeskAccessory(theMenu, theItem);
  413.             }
  414.             break;
  415.         case kFileMenu:
  416.             if (theItem == kQuitItem) {
  417.                 ExitToShell();
  418.             }
  419.             break;
  420.         default:
  421.             break;
  422.     }
  423.     
  424.     HiliteMenu(0);
  425. }
  426.  
  427. /*    --- DoContentClick ------------------------------------------------------------------------------
  428.     This routine handles clicks in the content area of a window. If the option key is down,
  429.     drag the launch window to another location on the screen.
  430. */
  431. static void
  432. DoContentClick(event, window)
  433. EventRecord *event;
  434. WindowPtr window;
  435. {
  436. #pragma unused(event)
  437.  
  438.     GrafPtr savePort;
  439.     Point clickPt;
  440.     
  441.     /*
  442.         If the clicked window isn't frontmost, simply select it
  443.     */
  444.     if (window != FrontWindow()) {
  445.         SelectWindow(window);
  446.         return;
  447.     }
  448.     
  449.     GetPort(&savePort);                    // save the current port
  450.     SetPort(window);                    // set the port to the clicked window
  451.         
  452.     clickPt = event->where;                // convert point to local coordindates
  453.     GlobalToLocal(&clickPt);
  454.     
  455.     DragWindow(window, event->where, &qd.screenBits.bounds);
  456.         
  457.     SetPort(savePort);                    // restore the saved port
  458. }
  459.  
  460. /*    ---------------------------------------------------------------------------------
  461.     DoSuspend
  462.  
  463.     This routine handles multifinder suspend events.
  464. */
  465. static void
  466. DoSuspend(event)
  467. EventRecord *event;
  468. {
  469. #pragma unused(event)
  470.     DoActivateWindow(false, gLevelWindow);
  471. }
  472.  
  473. /*    ---------------------------------------------------------------------------------
  474.     DoResume
  475.     
  476.     This routine handles multifinder resume events.
  477. */
  478. static void
  479. DoResume(event)
  480. EventRecord *event;
  481. {
  482. #pragma unused(event)
  483.     InitCursor();
  484.     DoActivateWindow(true, gLevelWindow);
  485. }
  486.  
  487. /*    ---------------------------------------------------------------------------------
  488.     DoDeskAccessory
  489.     
  490.     Handle desk accessory selections from the apple menu
  491. */
  492.  
  493. static void
  494. DoDeskAccessory(whichMenu, whichItem)
  495. short whichMenu;
  496. short whichItem;
  497. {
  498.     Str255 daName;
  499.     
  500.     GetMenuItemText(GetMenuHandle(whichMenu), whichItem, daName);
  501.         
  502.     (void) OpenDeskAcc(daName);
  503. }
  504.  
  505. /*    ---------------------------------------------------------------------------------
  506.     NewLevelWindow
  507.  
  508.     This routine will create the sound level window.
  509. */
  510.  
  511. static void
  512. NewLevelWindow()
  513. {
  514.     Point **userOffsetPtHndl;
  515.     Rect screenRect;
  516.     Rect globalRect;
  517.     
  518.     if (ColourQDExists())
  519.         gLevelWindow = GetNewCWindow(128, nil, (WindowPtr)-1);
  520.     else
  521.         gLevelWindow = GetNewWindow(128, nil, (WindowPtr)-1);
  522.         
  523.     SetPort(gLevelWindow);
  524.     
  525.     // load the offset resource that tells us if the user has dragged
  526.     // the window somewhere else. make sure the window remains visible
  527.     // and eventually put this in a pref file where it belongs
  528.     
  529.     userOffsetPtHndl = (Point **)GetResource('WPos', 128);
  530.     if (userOffsetPtHndl != nil) {
  531.         screenRect = qd.screenBits.bounds;
  532.         InsetRect(&screenRect, 5, 5);        // a little bit of a margin
  533.         
  534.         // the port is set for our window, so convert (0,0) to global for the
  535.         // global topLeft of the window
  536.         globalRect.left = (**userOffsetPtHndl).h;
  537.         globalRect.top = (**userOffsetPtHndl).v;
  538.         globalRect.right = globalRect.left + (gLevelWindow->portRect.right - gLevelWindow->portRect.left);
  539.         globalRect.bottom = globalRect.top + (gLevelWindow->portRect.bottom - gLevelWindow->portRect.top);
  540.  
  541.         if (SectRect(&screenRect, &globalRect, &globalRect)) {
  542.             MoveWindow(gLevelWindow, globalRect.left, globalRect.top, true);
  543.         } 
  544.         
  545.         ReleaseResource((Handle)userOffsetPtHndl);
  546.     }
  547.     
  548.     ShowWindow(gLevelWindow);            // make it visible
  549. }
  550.  
  551. /*    ---------------------------------------------------------------------------------
  552.     CleanUp
  553.  
  554.     This routine will clean up before exiting to the Finder
  555. */
  556. static void
  557. CleanUp()
  558. {
  559.     Point **userOffsetPtHndl;
  560.     Point globalPt;
  561.     
  562.     CloseLevelMeter();                // close up the level meter
  563.  
  564.     // save the position of the top/left corner of the window in case the user
  565.     // has moved it. For now, back to the WPos resource in the app will have to do
  566.     userOffsetPtHndl = (Point **)GetResource('WPos', 128);
  567.     if (userOffsetPtHndl != nil) {
  568.         SetPort(gLevelWindow);
  569.         globalPt.h = globalPt.v = 0;
  570.         LocalToGlobal(&globalPt);
  571.         
  572.         **userOffsetPtHndl = globalPt;
  573.         ChangedResource((Handle)userOffsetPtHndl);
  574.         WriteResource((Handle)userOffsetPtHndl);
  575.     }
  576.     if (gLevelWindow != nil)
  577.         DisposeWindow(gLevelWindow);
  578. }
  579.  
  580. /*    ---------------------------------------------------------------------------------
  581.     MakeCursor
  582.     
  583.     This routine will set the cursor to the 'CURS' resource whose id is given.
  584. */
  585. void
  586. MakeCursor(cursorID)
  587. short cursorID;
  588. {
  589. #pragma unused(cursorID)
  590. }
  591.  
  592.  
  593. /*    ---------------------------------------------------------------------------------
  594.     DoIdle
  595.     
  596. */
  597. static void
  598. DoIdle()
  599. {
  600.     OSErr err;
  601.     short recordingStatus = 0;                        // status of recording session
  602.     short meterLevel = 0;                            // current meter level
  603.     unsigned long totalSamplesToRecord = 0;            // total number of samples
  604.     unsigned long numberOfSamplesRecorded = 0;        // number of samples recorded
  605.     unsigned long totalMsecsToRecord;
  606.     unsigned long numberOfMsecsRecorded;
  607.     GrafPtr savePort;
  608.     
  609.     GetPort(&savePort);
  610.     SetPort(gLevelWindow);
  611.     
  612.     /* get the sound input status    */
  613.     err = SPBGetRecordingStatus(gSoundInputRefNum,
  614.                                 &recordingStatus,
  615.                                 &meterLevel,
  616.                                 &totalSamplesToRecord,
  617.                                 &numberOfSamplesRecorded,
  618.                                 &totalMsecsToRecord,
  619.                                 &numberOfMsecsRecorded);
  620.              
  621.     DrawTheMeter(&gMeterRect, 
  622.                    gNumElements, 
  623.                    &gPeakLevel, 
  624.                    &gLastLevel, 
  625.                    &gPeakTimeOut, 
  626.                    meterLevel,
  627.                    gRedZone,
  628.                    &blueRGB,
  629.                    &redRGB,
  630.                    &blackRGB,
  631.                    gUseColour);
  632.                    
  633.     SetPort(savePort);
  634. }
  635.  
  636. /*    ---------------------------------------------------------------------------------
  637.     OptionTest
  638.     
  639.     Returns whether the option key is being pressed
  640. */
  641. static Boolean
  642. OptionTest()
  643. {
  644.     KeyMap theKeys;
  645.     
  646.     GetKeys(&theKeys);
  647.     
  648.     if (theKeys[1] & 4)
  649.         return(true);
  650.     else
  651.         return(false);
  652.  
  653. }
  654.  
  655. /*    ---------------------------------------------------------------------------------
  656.     CmdTest
  657.     
  658.     Returns whether the command (apple) key is being pressed
  659. */
  660. static Boolean
  661. CmdTest()
  662. {
  663.     KeyMap theKeys;
  664.     
  665.     GetKeys(&theKeys);
  666.     
  667.     if (theKeys[1] & 0x8000)
  668.         return(true);
  669.     else
  670.         return(false);
  671.  
  672. }
  673.  
  674. /*    ---------------------------------------------------------------------------------
  675.     ShiftTest
  676.     
  677.     Returns whether the shift key is being pressed
  678. */
  679. static Boolean
  680. ShiftTest()
  681. {
  682.     KeyMap theKeys;
  683.     
  684.     GetKeys(&theKeys);
  685.     
  686.     if (theKeys[1] & 1)
  687.         return(true);
  688.     else
  689.         return(false);
  690.  
  691. }
  692.  
  693. #define ROM85            (*(short *)0x28e)
  694. #define TWOHIGHMASK        0xc000
  695. #define COLOURQDEXISTS    !(ROM85 & TWOHIGHMASK)
  696.  
  697.  
  698. /*    ---------------------------------------------------------------------------------
  699.     ColourQDExists
  700.     
  701.     This routine is called to determine whether or not color quickdraw
  702.     exists in the current roms. It checks the lom memory global just
  703.     like Apple usually does…
  704. */
  705. Boolean
  706. ColourQDExists()
  707. {
  708.     return(COLOURQDEXISTS);
  709. }
  710.  
  711. /*    ---------------------------------------------------------------------------------
  712.     ColourQDIsOn
  713.     
  714.     This routine is called to determine if colour quickdraw exists
  715.     in the current roms and if the main screen is displaying color.
  716. */
  717.  
  718. #define QD_MIN_FOR_COLOUR    4
  719.  
  720. Boolean
  721. ColourQDIsOn()
  722. {
  723.     GDHandle maindevice;
  724.     PixMapHandle mainpix;
  725.     short depth;
  726.  
  727.     /*
  728.         First confirm that color is available in the roms
  729.     */
  730.     if (COLOURQDEXISTS) {
  731.         /*
  732.             Determine if the main device is displaying color
  733.         */
  734.         maindevice = GetMainDevice();
  735.         mainpix = (*maindevice)->gdPMap;
  736.         
  737.         /*
  738.             Check the depth of the pixel map
  739.         */
  740.         depth = (*mainpix)->pixelSize;
  741.         
  742.         if (depth < QD_MIN_FOR_COLOUR)
  743.             return(false);
  744.         else
  745.             return(true);
  746.     } else
  747.         return(false);
  748. }
  749.  
  750. short
  751. ColourDepth()
  752. {
  753.     GDHandle maindevice;
  754.     PixMapHandle mainpix;
  755.  
  756.     /*
  757.         First confirm that color is available in the roms
  758.     */
  759.     if (COLOURQDEXISTS) {
  760.         /*
  761.             Determine if the main device is displaying color
  762.         */
  763.         maindevice = GetMainDevice();
  764.         mainpix = (*maindevice)->gdPMap;
  765.         
  766.         /*
  767.             Check the depth of the pixel map
  768.         */
  769.         return (*mainpix)->pixelSize;
  770.     } else
  771.         return 1;
  772. }
  773.  
  774. /*    ---------------------------------------------------------------------------------
  775.     TwitchToFinder
  776.     
  777.     Switch back to Finder. Use the System 7.0 process manager if present
  778. */
  779. static void
  780. TwitchToFinder()
  781. {
  782.  
  783. }
  784.  
  785. /*    ---------------------------------------------------------------------------------
  786.     DrawTheMeter
  787.     
  788.     This routine is called to draw the volume meter during sound input.
  789. */
  790.  
  791. void
  792. DrawTheMeter(Rect *meterRect, short numElements, short *peakLevel, short *lastLevel, long *peakTimeout, short meterLevel, short redZone, RGBColor *blueRGB, RGBColor *redRGB, RGBColor *blackRGB, Boolean useColour)
  793. {
  794.     Rect barRect;
  795.     short i;
  796.     
  797.     PenNormal();
  798.     
  799.     // quantize the meterLevel based on number of elements
  800.     // in our bar chart
  801.     if (meterLevel > kMaxVolume)
  802.         meterLevel = kMaxVolume;
  803.         
  804.     meterLevel = (meterLevel * numElements) / kMaxVolume;
  805.     
  806.     // determine if this is a new peak value and erase the
  807.     // peak bar if it has timed out
  808.     if (meterLevel >= *peakLevel) {
  809.         *peakLevel = meterLevel;
  810.         *peakTimeout = TickCount() + kPeakTimeoutTicks; 
  811.     } else {
  812.         // has the current peak timed out?
  813.         if (*peakLevel > 0 && *peakTimeout <= TickCount()) {
  814.             if (*peakLevel >= *lastLevel) {
  815.             
  816.                 if (useColour)
  817.                     RGBForeColor(blackRGB);
  818.  
  819.                 GetBarRect(meterRect, *peakLevel, &barRect);
  820.                 PaintRect(&barRect);
  821.                 
  822.                 // erase the previous element (peak is two elements wide)
  823.                 if (*peakLevel > 1) {            // don’t erase a non-element
  824.                     GetBarRect(meterRect, *peakLevel - 1, &barRect);
  825.                     PaintRect(&barRect);
  826.                 }
  827.             }
  828.             *peakLevel = 0;
  829.         }
  830.     }
  831.         
  832.     // check whether the signal is now stronger than last time
  833.     if (meterLevel > *lastLevel) {
  834.         for (i = *lastLevel + 1; i <= meterLevel; i++) {
  835.             GetBarRect(meterRect, i, &barRect);                    // get the rect for this bar
  836.             
  837.             if (useColour) {                                    // watch out for colour
  838.                 if (i >= redZone)                                // are we beyond the clipping point?
  839.                     RGBForeColor(redRGB);                        // draw in red to show distortion
  840.                 else
  841.                     RGBForeColor(blueRGB);                        // draw in blue for normal signal
  842.                 PaintRect(&barRect);                            // fill the element in
  843.                 
  844.             } else
  845.                 EraseRect(&barRect);                            // no colour so white will have to do
  846.         }
  847.         *lastLevel = meterLevel;                                // remember for next time
  848.     }
  849.         
  850.     // check whether the signal level is now weaker than last time
  851.     // and if it is remove the rightmost two level bar currently on
  852.     if (meterLevel < *lastLevel) {
  853.         if (*peakLevel != 0) {
  854.             if (*lastLevel == *peakLevel)                // don’t erase the peak!
  855.                 *lastLevel -= 2;
  856.             if (*lastLevel == *peakLevel -1)
  857.                 *lastLevel -= 1;
  858.         }
  859.         
  860.         if (useColour)
  861.             RGBForeColor(blackRGB);
  862.  
  863.         for (i = 0; i < 2; i++) {
  864.             if (*lastLevel > 0) {
  865.                 GetBarRect(meterRect, *lastLevel, &barRect);
  866.                 PaintRect(&barRect);
  867.                 *lastLevel -= 1;
  868.             }
  869.             
  870.             if (*lastLevel < 0 )
  871.                 *lastLevel = 0;
  872.         }
  873.         
  874.     }
  875.  
  876.     if (useColour)
  877.         RGBForeColor(blackRGB);
  878.  
  879. }
  880.  
  881. /*    ---------------------------------------------------------------------------------
  882.     GetBarRect
  883. */
  884. void
  885. GetBarRect(Rect *meterRect, short whichBar, Rect *barRect)
  886. {
  887.     if (whichBar == 0) {
  888.         return;
  889.     }
  890.     
  891.     SetRect(barRect, meterRect->left + 2, 
  892.                      meterRect->top + 1, 
  893.                      meterRect->left + 4, 
  894.                      meterRect->bottom - 1);
  895.                      
  896.     OffsetRect(barRect, (whichBar - 1)* 3, 0);
  897. }
  898.  
  899. /*    -------------------------------------------------------------------------------
  900.     InitLevelMeter
  901. */
  902.  
  903. void
  904. InitLevelMeter()
  905. {
  906.     RGBColor **colorRsrcHndl;
  907.     OSErr err;
  908.     
  909.     // load the colours used for the bar chart
  910.     colorRsrcHndl = (RGBColor **)GetResource(128, 'RGBv');
  911.     if (colorRsrcHndl) {
  912.         blueRGB = **colorRsrcHndl;
  913.         ReleaseResource((Handle)colorRsrcHndl);
  914.     } else {
  915.         blueRGB.red = 39321;        // flourescant blue
  916.         blueRGB.green = 65535;
  917.         blueRGB.blue = 65535;
  918.     }
  919.     
  920.     colorRsrcHndl = (RGBColor **)GetResource(129, 'RGBv');
  921.     if (colorRsrcHndl) {
  922.         redRGB = **colorRsrcHndl;
  923.         ReleaseResource((Handle)colorRsrcHndl);
  924.     } else {
  925.         redRGB.red = 65535;            // peak level red
  926.         redRGB.green = 0;
  927.         redRGB.blue = 0;
  928.     }
  929.     
  930.     blackRGB.red = 0;
  931.     blackRGB.green = 0;
  932.     blackRGB.blue = 0;
  933.     
  934.     gMeterRect = gLevelWindow->portRect;
  935.     InsetRect(&gMeterRect, 2, 2);
  936.     
  937.     gNumElements = (gMeterRect.right - gMeterRect.left - 2) / 3;
  938.     if (gNumElements > 255)
  939.         gNumElements = 255;
  940.  
  941.     gPeakTimeOut = 0;
  942.     gPeakLevel = 0;
  943.     gLastLevel = 0;
  944.     gRedZone = (gNumElements * 2) / 3;
  945.  
  946.     // open the sound input device
  947.     err = OpenTheSoundDevice();
  948. }
  949.  
  950.  
  951. // watch out for other usages of the soundInputRefNum
  952.  
  953. static void
  954. CloseLevelMeter()
  955. {
  956.     OSErr err;
  957.     
  958.     err = SPBCloseDevice(gSoundInputRefNum);
  959.     gSoundInputRefNum = 0;
  960. }
  961.  
  962. #define kAboutDLOG            128
  963. #define kAboutMeterItem        4
  964.  
  965. short    gAboutPeakLevel;
  966. short    gAboutLastLevel;
  967. long    gAboutPeakTimeOut;
  968.  
  969. void
  970. DoAbout()
  971. {
  972.     DialogPtr dialog;
  973.     short itemHit;
  974.     GrafPtr savePort;
  975.     ModalFilterUPP aboutFilterProcUPP;
  976.     
  977.     /* create a UPP for our modal dialog filter */
  978.     aboutFilterProcUPP = NewModalFilterProc(AboutFilterProc);
  979.     
  980.     GetPort(&savePort);
  981.     dialog = GetNewDialog(kAboutDLOG, nil, (WindowPtr)-1);
  982.     SetPort(dialog);
  983.     
  984.     // ••• force an update event first
  985.     UpdateAboutDialog((WindowPtr)dialog);
  986.     
  987.     gAboutPeakLevel = 0;
  988.     gAboutLastLevel = 0;
  989.     gAboutPeakTimeOut = 0;
  990.     
  991.     do {
  992.         ModalDialog(aboutFilterProcUPP, &itemHit);
  993.     } while (itemHit != ok);
  994.     
  995.     SetPort(savePort);
  996.     DisposeDialog(dialog);
  997.     
  998.     DisposeRoutineDescriptor(aboutFilterProcUPP);
  999.  
  1000. }
  1001.  
  1002. OSErr OpenTheSoundDevice()
  1003. {
  1004.     OSErr err;
  1005.     short meterState;
  1006.     
  1007.     // set the default zone to the system heap so that the SI manager will
  1008.     // possibly get fooled into allocating there instead of my heap
  1009.     
  1010.     SetZone(SystemZone());
  1011.     err = SPBOpenDevice(nil, siWritePermission, &gSoundInputRefNum);
  1012.     if (err != noErr) 
  1013.         goto BailOut;
  1014.         
  1015.     // turn on sound metering
  1016.     meterState = 1;                // turn it on
  1017.     err = SPBSetDeviceInfo(gSoundInputRefNum, siLevelMeterOnOff, (char *)&meterState);
  1018.  
  1019. BailOut:
  1020.     SetZone(ApplicationZone());
  1021.     
  1022.     return err;
  1023. }
  1024.  
  1025. void
  1026. DrawOneBar(Rect *meterRect, short whichBar, Boolean useColour)
  1027. {
  1028.     Rect barRect;
  1029.     
  1030.     GetBarRect(meterRect, whichBar, &barRect);            // get the rect for this bar
  1031.     
  1032.     if (useColour) {                                    // watch out for colour
  1033.         if (whichBar >= gRedZone)                        // are we beyond the clipping point?
  1034.             RGBForeColor(&redRGB);                        // draw in red to show distortion
  1035.         else
  1036.             RGBForeColor(&blueRGB);                        // draw in blue for normal signal
  1037.         PaintRect(&barRect);                            // fill the element in
  1038.         
  1039.     } else
  1040.         EraseRect(&barRect);                            // no colour so white will have to do
  1041. }
  1042.  
  1043. /*    ---------------------------------------------------------------------------------
  1044.     AboutFilterProc
  1045.     
  1046. */
  1047.  
  1048. #define CRKey            0x0d
  1049. #define EnterKey         0x03
  1050. #define EscapeKey        0x1b
  1051.  
  1052. pascal Boolean
  1053. AboutFilterProc(DialogPtr dialog, EventRecord *event, short *item)
  1054. {
  1055.     unsigned char theKey;
  1056.     WindowPtr window;
  1057.     short itemType;
  1058.     Handle itemHndl;
  1059.     Rect itemRect;
  1060.     OSErr err;
  1061.     short recordingStatus = 0;                        // status of recording session
  1062.     short meterLevel = 0;                            // current meter level
  1063.     unsigned long totalSamplesToRecord = 0;            // total number of samples
  1064.     unsigned long numberOfSamplesRecorded = 0;        // number of samples recorded
  1065.     unsigned long totalMsecsToRecord;
  1066.     unsigned long numberOfMsecsRecorded;
  1067.         
  1068.     /* get the sound input status    */
  1069.     if (event->what != updateEvt) {
  1070.         err = SPBGetRecordingStatus(gSoundInputRefNum,
  1071.                                     &recordingStatus,
  1072.                                     &meterLevel,
  1073.                                     &totalSamplesToRecord,
  1074.                                     &numberOfSamplesRecorded,
  1075.                                     &totalMsecsToRecord,
  1076.                                     &numberOfMsecsRecorded);
  1077.                  
  1078.         GetDialogItem(dialog, kAboutMeterItem, &itemType, &itemHndl, &itemRect);
  1079.         InsetRect(&itemRect, 2, 2);
  1080.         
  1081.         SetPort(dialog);
  1082.         
  1083.         DrawTheMeter(&itemRect, 
  1084.                        gNumElements, 
  1085.                        &gAboutPeakLevel, 
  1086.                        &gAboutLastLevel, 
  1087.                        &gAboutPeakTimeOut, 
  1088.                        meterLevel,
  1089.                        gRedZone,
  1090.                        &blueRGB,
  1091.                        &redRGB,
  1092.                        &blackRGB,
  1093.                        gUseColour);
  1094.                        
  1095.         // check for nearly maximum volume to dismiss dialog;
  1096.         if (meterLevel > 250) {
  1097.             HiliteItem(dialog, ok);
  1098.             *item = ok;
  1099.             return true;
  1100.         }
  1101.         
  1102.     }
  1103.     
  1104.     switch(event->what) {
  1105.         case mouseDown:
  1106.             break;
  1107.         case keyDown:
  1108.         case autoKey:
  1109.             theKey = event->message & charCodeMask;
  1110.             
  1111.             if (theKey == CRKey || theKey == EnterKey || theKey == EscapeKey) {
  1112.                 HiliteItem(dialog, ok);
  1113.                 *item = ok;
  1114.                 return true;
  1115.             }
  1116.                         
  1117.             break;
  1118.         case updateEvt:
  1119.             window = (WindowPtr)event->message;
  1120.             if (window == (WindowPtr)dialog) {
  1121.                 UpdateAboutDialog(window);
  1122.             }
  1123.             break;
  1124.         default:
  1125.             break;
  1126.     }
  1127.     return false;
  1128. }
  1129.  
  1130. void
  1131. UpdateAboutDialog(WindowPtr window)
  1132. {
  1133.     GrafPtr savePort;
  1134.     short itemType;
  1135.     Handle itemHndl;
  1136.     Rect itemRect;
  1137.     
  1138.     BeginUpdate(window);
  1139.     GetPort(&savePort);
  1140.     SetPort(window);
  1141.     
  1142.     UpdateDialog((DialogPtr)window, window->visRgn);
  1143.     
  1144.     PenNormal();
  1145.     GetDialogItem((DialogPtr)window, kAboutMeterItem, &itemType, &itemHndl, &itemRect);
  1146.     FrameRect(&itemRect);
  1147.     InsetRect(&itemRect, 2, 2);
  1148.     PaintRect(&itemRect);
  1149.     
  1150.     SetPort(savePort);
  1151.     EndUpdate(window);
  1152. }
  1153.  
  1154. /*    ---------------------------------------------------------------------------------
  1155.     HiliteItem
  1156.     
  1157.     This routine will hilite the given item control so the user knows
  1158.     which item has been activated by the  key equivalent
  1159. */
  1160.  
  1161. #define ButtonInvertState    10        // inverted state for push buttons
  1162. #define CheckRadioState        11        // inverted state for checks and radios
  1163. #define NormalState            0        // non-inverted state
  1164. #define SleepDuration        10        // pause for 10 ticks
  1165.  
  1166. void
  1167. HiliteItem(DialogPtr dialog, short theitem)
  1168. {
  1169.     Handle thehndl;
  1170.     short thetype;
  1171.     Rect thebox;
  1172.     long junkTicks;
  1173.     short hiliteState;
  1174.     
  1175.     /*
  1176.         Get Handle to the item
  1177.     */
  1178.     GetDialogItem(dialog, theitem, &thetype, &thehndl, &thebox);
  1179.     
  1180.     switch (thetype) {
  1181.         case btnCtrl + ctrlItem:
  1182.             hiliteState = ButtonInvertState;
  1183.             break;
  1184.         case chkCtrl + ctrlItem:
  1185.         case radCtrl + ctrlItem:
  1186.             hiliteState = CheckRadioState;
  1187.             break;
  1188.         default:
  1189.             return;
  1190.             break;
  1191.     }
  1192.     /*
  1193.         Invert the control for a little while
  1194.     */
  1195.     HiliteControl((ControlHandle)thehndl, hiliteState);
  1196.     Delay(SleepDuration, &junkTicks);
  1197.     HiliteControl((ControlHandle)thehndl, NormalState);
  1198. }
  1199.